// QMAP

/* da config.js
var tile_server_url = "http://osm.qmap.it";
var api_server_url = "http://osm.qmap.it";
*/

var map;
var ajaxRequest;
var plotlist;
var plotlayers = [];

// il layer dei punti
var pointsLayer;
var alertsLayer;
var eventsLayer;

// array associativo dei parametri GET della richiesta
var urlParams;
// Url richiesta dall'utente
var url;

// Layer
var simpleLayer;
var speedLayer;
var anomaliesLayer;
var freeflowLayer;
var trafficLayer;

// MarkerCluster per gli eventi ed allerte
var eventsMarkerGroup;
var alertsMarkerGroup;

// Layer Url
var speedTrafficUrl;
var anomaliesTrafficUrl;
var freeflowTrafficUrl;
var trafficUrl;

/**
 * Modi di visualizzazione del traffico
 */
var modes = {
    SPEED: 0,
    TRAFFIC: 1,
    ANOMALIES: 2,
    FREEFLOW: 3
};
// Layer attivo di default = SPEED
var selectedMode = modes.SPEED;

// Booleano per attivare/disattivare gli eventi
var enable_event = false;
// Booleano per attivare/disattivare le allerte
var enable_alert = false;

/**
 * I layers mappa base
 * 
 * @type type
 */
var baseMaps;
/**
 * I layer di QMap
 * 
 * @type type
 */
var groupedExclusiveOverlays;

var LOADING_STRING = "<br/><br/><br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Loading...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/><br/><br/><br/><br/>";

// Layer aggiornato
var newlayer;

// il div dove mettere la data di ultimo aggiornamento dei dati
var updateTimeDiv;
// ID dell'ultimo aggiornamento effettuato
var releaseId = 0;
//fullscreen?
var fs = 0;

// mette i parametry della query HTTP nell'array urlParams
(window.onpopstate = function() {
    var match, pl = /\+/g, // Regex for replacing addition symbol with a space
            search = /([^&=]+)=?([^&]*)/g, decode = function(s) {
        return decodeURIComponent(s.replace(pl, " "));
    }, query = window.location.search.substring(1);

    urlParams = {};
    while (match = search.exec(query))
        urlParams[decode(match[1])] = decode(match[2]);
    //alert("urlParams:"+JSON.stringify(urlParams));
})();

function initmap() {

    if (urlParams['fs']) {
        fs = parseInt(urlParams['fs']);
    }
    if (fs == 1) {
        // nascondo tutti gli elementi dell'interfaccia
        zoom_control = false;
        attribution_control = false;
        base_layer_controls = false;
    }

    // set up the map
    map = new L.Map('map', {
        zoomControl: zoom_control,
        attributionControl: attribution_control
    });

    updateTimeDiv = $('.leaflet-top .leaflet-left');
    // Osm Layer
    var osmUrl = tile_server_url + '/osm_tiles/{z}/{x}/{y}.png';
    var osm = loadLayer(osmUrl, null, -1);

    // Simple Layer
    var simpleUrl = tile_server_url + '/simple_map/{z}/{x}/{y}.png';
    simpleLayer = loadLayer(simpleUrl, null, -1);

    // Night Layer
    var nightLayerUrl = 'http://a.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/999/256/{z}/{x}/{y}.png';
    nightLayer = loadLayer(nightLayerUrl, null, -1);
    // map.addLayer(nightLayer);

    // Speed Layer
    speedTrafficUrl = tile_server_url + '/qmap_speed' + slices_grp_url
            + '/{z}/{x}/{y}.png?ts={timestamp}';
    speedLayer = loadLayer(speedTrafficUrl, modes.SPEED, 10);

    // Anomalies Layer
    anomaliesTrafficUrl = tile_server_url + '/qmap_anomalies' + slices_grp_url
            + '/{z}/{x}/{y}.png?ts={timestamp}';
    anomaliesLayer = loadLayer(anomaliesTrafficUrl, modes.ANOMALIES, 10);

    // FreeFlow Layer
    freeflowTrafficUrl = tile_server_url + '/qmap_freeflow' + slices_grp_url
            + '/{z}/{x}/{y}.png?ts={timestamp}';
    freeflowLayer = loadLayer(freeflowTrafficUrl, modes.FREEFLOW, 10);

    // Traffic real time layer
    trafficUrl = tile_server_url + '/qmap_traffic' + slices_grp_url
            + '/{z}/{x}/{y}.png?ts={timestamp}';
    trafficLayer = loadLayer(trafficUrl, modes.TRAFFIC, 10);

     if (urlParams['mode']) {
        selectedMode = parseInt(urlParams['mode']);
        switch (selectedMode) {
            case modes.SPEED:
                map.addLayer(speedLayer);
                break;
            case modes.TRAFFIC:
                map.addLayer(trafficLayer);
                break;
            case modes.ANOMALIES:
                map.addLayer(anomaliesLayer);
                break;
            case modes.FREEFLOW:
                map.addLayer(freeflowLayer);
                break;
        }
    } else {
        selectedMode = modes.SPEED;
        map.addLayer(speedLayer);
    }


    // points è definito dal template twig
    if (points) {
        pointsLayer = L.geoJson().addTo(map);
        map.on('moveend', loadPoints);
        map.on('exclusivelayerchange', loadPoints);
        loadPoints();
    }

    eventsMarkerGroup = new L.MarkerClusterGroup({
        maxClusterRadius: 32,
        iconCreateFunction: function(cluster) {
            return new L.divIcon({
                html: "<b>" + cluster.getChildCount() + "</b>",
                className: 'eventCluster',
                iconSize: L.point(32, 32)
            });
        }
    });

    alertsMarkerGroup = new L.MarkerClusterGroup({
        maxClusterRadius: 32,
        iconCreateFunction: function(cluster) {
            return new L.divIcon({
                html: "<b>" + cluster.getChildCount() + "</b>",
                className: 'alertCluster',
                iconSize: L.point(25, 41)
            });
        }
    });

    // centro la mappa e definisco i vari tiles di base e di overlay //
    baseMaps = {
        "OpenStreetMap": osm,
        "Simple": simpleLayer,
        "Night": nightLayer
    };

    alertsLayer = L.geoJson();
    eventsLayer = L.geoJson();

    var groupedOverlays = {
        "Poi": {
            "eventi": eventsMarkerGroup,
            "allerte": alertsMarkerGroup
        }
    };

    groupedExclusiveOverlays = {
        "Traffico": {
            "velocità": speedLayer,
            "traffico": trafficLayer,
            "anomalie": anomaliesLayer,
            "free flow": freeflowLayer
        }
    };

    // valori di default
    var lat = 41.53325;
    var lon = 12.32666;
    var zl = 6;
    if (urlParams['lat']) {
        lat = urlParams['lat'];
    }
    if (urlParams['lon']) {
        lon = urlParams['lon'];
    }
    if (urlParams['zl'] >= 6 && urlParams['zl'] <= 16) {
        zl = urlParams['zl'];
    }


    map.setView(new L.LatLng(lat, lon), zl);

   
    if (urlParams['bg']) {
        var baseLayerName = getKey(baseMaps, urlParams['bg']);
        map.addLayer(baseMaps[baseLayerName]);
    } else {
        map.addLayer(nightLayer);
    }
    // verifico quali poi sono attivati
    if (urlParams['poi']) {
        // uso una maskera binaria col seguente ordine di bit: alerts, events
        if ((urlParams['poi'] & 1) != 0) {
            enable_alert = true;
        } else {
            enable_alert = false;
        }
        var diff = urlParams['poi'] & (1 << 1);
        if ((urlParams['poi'] & 2) != 0) {
            enable_event = true;
        } else {
            enable_event = false;
        }
    }

    map.on('moveend', loadAlerts);
    loadAlerts();
    map.on('moveend', loadEvents);
    loadEvents();

    // initBar();


    L.control.groupedLayers(baseMaps, groupedExclusiveOverlays,
            groupedOverlays, {
        autoZIndex: false,
        collapsed: false
    }).addTo(map);

    map.on('overlayadd', function(layer) {
        var s = layer.name;
        if (s.indexOf("eventi") !== -1) // trovato
        {
            if (!eventsMarkerGroup.hasLayer(eventsLayer)) {
                eventsMarkerGroup.addLayer(eventsLayer);
            }
            map.addLayer(eventsMarkerGroup);
            enable_event = true;
            loadEvents();
        } else if (s.indexOf("allerte") !== -1) {
            if (!alertsMarkerGroup.hasLayer(alertsLayer)) {
                alertsMarkerGroup.addLayer(alertsLayer);
            }
            map.addLayer(alertsMarkerGroup);
            enable_alert = true;
            loadAlerts();
        }

    });

    map.on('overlayremove', function(layer) {
        var s = layer.name;
        if (s.indexOf("eventi") !== -1) // trovato
        {
            eventsLayer.clearLayers();
            eventsMarkerGroup.clearLayers();
            map.removeLayer(eventsLayer);
            enable_event = false;
        } else if (s.indexOf("allerte") !== -1) {
            alertsLayer.clearLayers();
            alertsMarkerGroup.clearLayers();
            map.removeLayer(alertsLayer);
            enable_alert = false;
        }

    });
    if (!base_layer_controls) {
        $(".leaflet-top").hide();
    }
    // refreshLayer();
    // initUpdate();
    refreshLastUpdate();

    map.on('moveend', update_browser_query);
    map.on('baselayerchange', update_browser_query);
    map.on('overlayadd', update_browser_query);
    map.on('overlayremove', update_browser_query);
    map.on('exclusivelayerchange', update_browser_query);

}

/**
 * Carica i layer
 * 
 * @param url
 * @param mode
 * @param z_index
 * @returns {L.TileLayer}
 */
function loadLayer(url, mode, z_index) {
    var attribution = 'Map data © OpenStreetMap contributors';

    if (mode == modes.SPEED || mode == modes.TRAFFIC
            || modes == modes.ANOMALIES || mode == modes.FREEFLOW) {
        attribution = 'Traffic data © QMap s.r.l.';
    }

    if (!z_index) {
        z_index = 1;
    }

    var layer = new L.TileLayer(url, {
        minZoom: 6,
        maxZoom: 16,
        attribution: attribution,
        zIndex: z_index,
        timestamp: releaseId
    });

    return layer;

}

function refreshLastUpdate() {
    var url = api_server_url + '/api/lastupdate';
    $.ajax({
        url: url,
        dataType: 'jsonp',
        success: function(json) {
            updateTimeDiv.html("<div class='lastupdate'>Ultimo aggiornamento: "
                    + json.lastUpdate.substring(0, json.lastUpdate.length - 3)
                    + "</div>");
            updateTraffic(json.releaseId);
        },
        error: function(jqXHR, textStatus, errorThrown) {
            updateTimeDiv.html("Errore update");
        }
    });
}

/**
 * Se il releaseId è cambiato aggiorna i layer altrimenti attende 10 secondi
 * 
 * @param release
 */
function updateTraffic(release) {
    if (releaseId < release) {
        if (releaseId === 0) {
            var firstRun = true;
        }
        releaseId = release;
        //Aggiorna i layer di traffico e poi attende 170 secondi
        refreshLayer(release);
        //console.log(releaseId);
        if (firstRun) {//al primo giro ricarico ogni 10 secondi
            setTimeout(refreshLastUpdate, 10000);
        } else {//poi aspetto 2:50 minuti per il refresh successivo
            setTimeout(refreshLastUpdate, 170000);
        }

    } else {
        //Controlla se il releaseID è stato aggiornato ogni 10 secondi
        setTimeout(refreshLastUpdate, 10000);
    }
}

/**
 * Inizializza il releaseId all'avvio dell'applicazione, funzione chiamata
 * all'inizializzazione della mappa
 */
function initUpdate() {
    var url = api_server_url + '/api/lastupdate';
    $.ajax({
        url: url,
        dataType: 'jsonp',
        success: function(json) {
            releaseId = json.releaseId;
        },
        error: function(jqXHR, textStatus, errorThrown) {
            updateTimeDiv.html("Errore update");
        }
    });
}

/**
 * Carica i punti attraverso richesta ajax JSON/JSONP
 * 
 * @returns {undefined}
 */

function loadPoints() {

    if (map.hasLayer(pointsLayer)) {
        var zoom = map.getZoom();
        var pointIconUrl = 'images/point.png';
        //var pointIconUrl = '/bundles/pklossmapclient/images/point.png';

        var geojsonPointOptions = {
            pointToLayer: function(feature, latlng) {
                var lcdFrom = feature.properties.lcd1;
                var lcdTo = feature.properties.lcd2;
                var roaLcd = feature.properties.roa_lcd;
                var roadDir = feature.properties.road_dir;
                var seqnr = feature.properties.seqnr;

                var pointMarker = L.marker(latlng, {
                    icon: L.icon({
                        iconUrl: pointIconUrl,
                        iconSize: [6, 6],
                        iconAnchor: [3, 3],
                        popupAnchor: [0, 0]
                    })
                });

                pointMarker.bindPopup(LOADING_STRING, options = {
                    maxWidth: 500,
                    autoPan: false
                });

                var basePath = '/api/point/';
                if (selectedMode == modes.FREEFLOW) {
                    basePath = '/api/freeflow/';
                }
                var url = api_server_url + basePath + lcdFrom + '/' + lcdTo
                        + '/' + seqnr + '/' + roaLcd + '/' + roadDir+".json";

                pointMarker.on('click', function(e) {
                    $.ajax({
                        url: url,
                        dataType: 'jsonp',
                        type: 'GET',
                        success: function(data) {
                            $(".leaflet-popup-content").html("<div>"+JSON.stringify(data)+"</div>");
                        },
                        error: function(jqXHR, textStatus, errorThrown) {
                            $(".leaflet-popup-content").html(
                                    "Errore nel caricamento dati dal server: "
                                    + errorThrown);
                        }
                    });

                });

                return pointMarker;
            }
        };

        if (zoom > 11) {
          /**
             * @type LatLngBounds
             */
            var bounds = map.getBounds();

            var url = api_server_url + '/api/points/' + zoom + '/'
                    + bounds.toBBoxString();

            $.ajax({
                url: url,
                dataType: 'jsonp',
                success: function(features) {
                    pointsLayer.clearLayers();
                    pointsLayer = L.geoJson(features, geojsonPointOptions)
                            .addTo(map);

                },
                error: function(jqXHR, textStatus, errorThrown) {
                    pointsLayer.clearLayers();
                }
            });
        } else {
            pointsLayer.clearLayers();
        }
    }
}

/**
 * Carica gli Alerts attraverso richesta ajax JSON/JSONP
 * 
 * @returns {undefined}
 */
function loadAlerts() {
    if (enable_alert) {
        var zoom = map.getZoom();

        //var alertIconUrl_1 = '/bundles/pklossmapclient/images/alert1.png';
        var alertIconUrl_1 = 'images/alert1.png';
        var alertIconUrl_2 = 'images/alert2.png';
        var alertIconUrl_3 = 'images/alert3.png';

        var geojsonAlertOptions = {
            pointToLayer: function(feature, latlng) {
                var levelId = feature.properties.level_id;
                var lcdFrom = feature.properties.lcd_from;
                var lcdTo = feature.properties.lcd_to;
                var roaLcd = feature.properties.roa_lcd;
                var roadDir = feature.properties.road_dir;

                var iconUrl = '';

                if (levelId == 1) {
                    iconUrl = alertIconUrl_1;
                } else if (levelId == 2) {
                    iconUrl = alertIconUrl_2;
                } else if (levelId == 3) {
                    iconUrl = alertIconUrl_3;
                }

                var pointMarker = L.marker(latlng, {
                    icon: L.icon({
                        iconUrl: iconUrl,
                        iconSize: [25, 41],
                        iconAnchor: [13, 41],
                        popupAnchor: [0, -41]
                    })
                });
                pointMarker.bindPopup(LOADING_STRING, options = {
                    maxWidth: 500,
                    autoPan: false
                });

                var url = api_server_url + '/api/anomaly/' + lcdFrom + '/'
                        + lcdTo + '/' + roaLcd + '/' + roadDir+".json";
                
                pointMarker.on('click', function(e) {
                    $.ajax({
                        url: url,
                        dataType: 'jsonp',
                        type : 'GET',
                        success: function(data) {
                            $(".leaflet-popup-content").html("<div>"+JSON.stringify(data)+"</div>");
                        },
                        error: function(jqXHR, textStatus, errorThrown) {
                            $(".leaflet-popup-content").html(
                                    "Errore nel caricamento dati dal server: "
                                    + errorThrown);
                        }
                    });

                });

                return pointMarker;
            }
        };

        if (zoom >= 6) { // in pratica sempre
            /**
             * @type LatLngBounds
             */
            var bounds = map.getBounds();

            var url = api_server_url + '/api/alerts/' + zoom + '/'
                    + bounds.toBBoxString();
            //alert(url);
            $.ajax({
                url: url,
                dataType: 'jsonp',
                success: function(features) {
                    alertsLayer.clearLayers();
                    // alertsLayer = L.geoJson(features, geojsonAlertOptions)
                    // .addTo(map);
                    alertsLayer = L.geoJson(features, geojsonAlertOptions);
                    alertsMarkerGroup.clearLayers();
                    alertsMarkerGroup.addLayer(alertsLayer);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    alertsLayer.clearLayers();
                }
            });
        } else {
            alertsLayer.clearLayers();
        }
        map.addLayer(alertsMarkerGroup);

    }
}

/**
 * Carica gli Events attraverso richesta ajax JSON/JSONP
 * 
 * @returns {undefined}
 */
function loadEvents() {
    if (enable_event) {
        // if (map.hasLayer(eventsMarkerGroup)) {
        var zoom = map.getZoom();

        var eventsIconUrl = '';

        var geojsonEventOptions = {
            pointToLayer: function(feature, latlng) {

                var lcdFrom = feature.properties.lcd_from;
                var lcdTo = feature.properties.lcd_to;
                var roaLcd = feature.properties.roa_lcd;
                var ogc_fid = feature.properties.ogc_fid;
                var dir = feature.properties.dir;

                var eventTypeId = feature.properties.event_type_id;

                eventsIconUrl = api_server_url + '/api/eventicons/'
                        + eventTypeId;

                var pointMarker = L.marker(latlng, {
                    icon: L.icon({
                        iconUrl: eventsIconUrl,
                        iconSize: [25, 25],
                        iconAnchor: [13, 25],
                        popupAnchor: [0, -25]
                    })
                });

                pointMarker.bindPopup(LOADING_STRING, options = {
                    maxWidth: 500,
                    autoPan: false
                });

                var url = api_server_url + '/api/event/' + ogc_fid + '/'
                        + roaLcd + '/' + dir + '/' + lcdFrom + '/' + lcdTo+".json";
                pointMarker.on('click', function(e) {
                    $.ajax({
                        url: url,
                        dataType: 'jsonp',
                        type: 'GET',
                        success: function(data) {
                            $(".leaflet-popup-content").html(
                            		"<h3>"+data.std_roadnumber+" "+data.std_roadname+"</h3>"+
                            		"<img width='25px' height='25px' src='http://osm.qmap.it/api/eventicons/"+data.event_type_id+"'>"+
                            		"<img width='25px' height='25px' src='http://osm.qmap.it/api/eventicons/"+data.cause_id+"'> <br>"+
                            		data.event_name+"<br>"+
                            		"<i>Inizio</i>:"+data.name_from+"<br>"+
                            		"<i>Fine</i>:"+data.name_to+"<br>"+
                            		"<i>Direzione</i>:"+data.std_road_direction_name+"<br>"+
                            		"<i>Descrizione</i>:"+data.further_info+"<br>"+
                            		"<i>Inizio segnalazione</i>:"+data.start_date+"<br>"+
                            		"<i>Fine segnalazione</i>:"+data.end_date+"<br>"+
                            		"<i>Ultimo aggiornamento</i>:"+data.update_date+"<br>"
                            );
                        
                        },
                        error: function(jqXHR, textStatus, errorThrown) {
                            $(".leaflet-popup-content").html(
                                    "Errore nel caricamento dati dal server: "
                                    + errorThrown);
                        }
                    });

                });

                return pointMarker;
            }
        };

        if (zoom >= 6) { // in pratica sempre
            /**
             * @type LatLngBounds
             */
            var bounds = map.getBounds();

            var url = api_server_url + '/api/events/' + zoom + '/'
                    + bounds.toBBoxString();

            // eventsMarkerGroup.removeLayer(eventsLayer);
            $.ajax({
                url: url,
                dataType: 'jsonp',
                success: function(features) {
                    eventsLayer.clearLayers();
                    eventsLayer = L.geoJson(features, geojsonEventOptions);
                    eventsMarkerGroup.clearLayers();
                    eventsMarkerGroup.addLayer(eventsLayer);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    eventsLayer.clearLayers();
                }
            });
        } else {
            eventsLayer.clearLayers();
        }
        map.addLayer(eventsMarkerGroup);
    }

}

function update_browser_query(le) {
    // se le è un LayerEvent cerco di capire quale baseLayer è visualizzato
    var bg = 2;
    if (le && (le.type == 'baselayerchange') && le.layer) {
        var j = 0;
        for (l in baseMaps) {
            if (baseMaps[l] == le.layer) {
                var bg = j;
                break;
            }
            j++;
        }
    } else {
        if (urlParams['bg']) {
            bg = urlParams['bg'];
        }
    }
    if (le && (le.type == 'exclusivelayerchange')) {
        var j = 0;
        for (l in groupedExclusiveOverlays['Traffico']) {
            if (groupedExclusiveOverlays['Traffico'][l] == le.layer) {
                selectedMode = j;
                break;
            }
            j++;
        }
    }

    var poi = enable_alert + (enable_event << 1);

    window.history.replaceState({}, 'QMap Traffic', window.location.pathname
            + '?fs=' + fs + '&mode=' + selectedMode + '&poi=' + poi + '&bg='
            + bg + '&zl=' + map.getZoom() + '&lon=' + map.getCenter().lng
            + '&lat=' + map.getCenter().lat);
}

/**
 * Ritorna la chiave dell'ennesimo elemento dell'array associativo
 * 
 * @param array
 *            obj
 * @param integer
 *            i
 * @returns string
 */
function getKey(obj, i) {
    var j = 0;
    for (var p in obj) {
        if (j++ == i)
            return p;
    }
    return null;
}

/**
 * Ricarica i layer attivi
 */
function refreshLayer() {
    speedLayer.options.timestamp = releaseId;
    trafficLayer.options.timestamp = releaseId;
    anomaliesLayer.options.timestamp = releaseId;
    freeflowLayer.options.timestamp = releaseId;
    switch (selectedMode) {
        case modes.SPEED:
            speedLayer.redraw();
            break;
        case modes.TRAFFIC:
            trafficLayer.redraw();
            break;
        case modes.ANOMALIES:
            anomaliesLayer.redraw();
            break;
        case modes.FREEFLOW:
            freeflowLayer.redraw();
            break;
    }
    if (points) {
        loadPoints();
    }
    if (enable_alert) {
        loadAlerts();
    }
    if (enable_event) {
        loadEvents();
    }
}